﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
using QW.Core.Domain;

namespace QW.Core.RunHelper
{
    /// <summary>
    /// 服务安装
    /// </summary>
    public class ServiceInstall
    {
        /// <summary>
        /// Windows 环境下运行
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="serviceName"></param>
        /// <param name="displayName"></param>
        /// <param name="serviceDescription"></param>
        /// <param name="args"></param>
        /// <param name="startRun"></param>
        /// <returns></returns>
        private static bool RunWin(string filePath, string serviceName, string displayName, string serviceDescription, string[] args, Action<string[]> startRun)
        {
            if (args.Length == 1)
            {
                Path.GetDirectoryName(filePath);
                if (System.IO.File.Exists(Path.ChangeExtension(filePath, ".exe")))
                    filePath = Path.ChangeExtension(filePath, ".exe");
                else
                {
                    Environment.CurrentDirectory = Path.GetDirectoryName(typeof(ServiceInstall).Assembly.Location);
                    var text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe");
                    if (System.IO.File.Exists(text))
                        filePath = "\\\"" + text + "\\\" \\\"" + filePath + "\\\"";
                    else
                    {
                        text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "dotnet", "dotnet.exe");
                        if (!System.IO.File.Exists(text))
                        {
                            Console.WriteLine("系统无法定位DotNet Core的安装目录。");
                            return true;
                        }

                        filePath = "\\\"" + text + "\\\" \\\"" + filePath + "\\\"";
                    }
                }

                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                        return true;

                    Console.WriteLine(StartProcess("sc.exe", "create " + serviceName + " binPath= \"" + filePath + "\" start= auto"));
                    Console.WriteLine("Windows 下安装服务完成，如果失败请手动执行以下命令完成安装:");
                    Console.WriteLine("sc create " + serviceName + " binpath=\"" + filePath + "\" start=auto DisplayName=\"" + displayName + "\" //安装服务");
                    using (var registryKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\" + serviceName, writable: true))
                    {
                        registryKey.SetValue("DisplayName", displayName);
                        registryKey.SetValue("Description", serviceDescription);

                        // 构建 FailureActions 的二进制数据（前两次重启，第三次无操作）
                        var delaySeconds = 60;  // 重启等待秒数（需转换为毫秒，Windows使用毫秒单位）
                        int delayMilliseconds = delaySeconds * 1000;  // 关键修正：时间单位是毫秒而非秒

                        // 1. 头部（8字节）：重置失败计数时间（单位：秒），0表示不自动重置
                        byte[] resetTime = BitConverter.GetBytes(0);  // 4字节数值，小端序
                        if (!BitConverter.IsLittleEndian) Array.Reverse(resetTime);
                        byte[] header = resetTime.Concat(new byte[4]).ToArray();  // 补4字节保留位，共8字节

                        // 2. 操作数量与保留字段（8字节）：前4字节为保留位(0)，后4字节为操作数量(3)
                        byte[] reserved = new byte[4];  // 必须为00 00 00 00
                        byte[] actionCount = BitConverter.GetBytes(3);  // 3次操作
                        if (!BitConverter.IsLittleEndian) Array.Reverse(actionCount);
                        byte[] actionCountWithReserved = reserved.Concat(actionCount).ToArray();  // 共8字节

                        // 转换等待时间为4字节小端序（毫秒单位）
                        byte[] delayBytes = BitConverter.GetBytes(delayMilliseconds);
                        if (!BitConverter.IsLittleEndian) Array.Reverse(delayBytes);  // 确保小端序

                        // 3. 第1次故障操作（8字节）：[4字节时间][4字节动作]，动作1=重启
                        byte[] firstAction = delayBytes.Concat(new byte[] { 0x01, 0x00, 0x00, 0x00 }).ToArray();

                        // 4. 第2次故障操作（8字节）：同上（重启）
                        byte[] secondAction = delayBytes.Concat(new byte[] { 0x01, 0x00, 0x00, 0x00 }).ToArray();

                        // 5. 第3次故障操作（8字节）：[4字节时间][4字节动作]，动作0=无操作
                        byte[] thirdAction = delayBytes.Concat(new byte[] { 0x00, 0x00, 0x00, 0x00 }).ToArray();

                        // 6. 结束标记（4字节，固定0）
                        byte[] endMarker = new byte[4];

                        // 合并所有部分为完整的44字节二进制数组
                        var failureActions = header
                            .Concat(actionCountWithReserved)
                            .Concat(firstAction)
                            .Concat(secondAction)
                            .Concat(thirdAction)
                            .Concat(endMarker)
                            .ToArray();

                        // 设置注册表值（REG_BINARY类型）
                        registryKey.SetValue("FailureActions", failureActions, RegistryValueKind.Binary);

                        // 3. 可选：设置失败计数重置时间（ResetPeriod，单位秒,86400=1天）
                        registryKey.SetValue("ResetPeriod", 86400, RegistryValueKind.DWord);
                    }

                    Console.WriteLine("启动服务");
                    Console.WriteLine(StartProcess("net.exe", "start " + serviceName));
                    return true;
                }

                if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                {
                    if (!AdminRestartApp(filePath, args))
                        return true;

                    Console.WriteLine("停止服务");
                    try { Console.WriteLine(StartProcess("net.exe", "stop " + serviceName)); } catch (Exception ex) { Console.WriteLine($"[错误]{ex.Message}"); }
                    Console.WriteLine("删除服务");
                    try { Console.WriteLine(StartProcess("sc.exe", "delete " + serviceName)); } catch (Exception ex) { Console.WriteLine($"[错误]{ex.Message}"); }
                    Console.WriteLine("Windows 下卸载服务完成，如果失败请手动执行以下命令完成卸载:");
                    Console.WriteLine("sc delete " + serviceName + "  //卸载服务");
                    return true;
                }
            }

            WinService.Config(startRun, serviceName);
            using (var service = new WinService())
            {
                ServiceBase.Run(service);
            }
            return false;
        }

        /// <summary>
        /// Unix环境下运行
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="serviceName"></param>
        /// <param name="serviceDescription"></param>
        /// <param name="args"></param>
        /// <param name="startRun"></param>
        /// <returns></returns>
        private static bool RunUnix(string filePath, string serviceName, string serviceDescription, string[] args, Action<string[]> startRun)
        {
            var text = "/usr/share/dotnet/dotnet " + Path.GetFileName(filePath);
            if (System.IO.File.Exists("/usr/bin/dotnet"))
                text = "/usr/bin/dotnet " + Path.GetFileName(filePath);

            if (!Process.GetCurrentProcess().MainModule.FileName.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase))
            {
                filePath = Process.GetCurrentProcess().MainModule.FileName;
                text = filePath;
            }

            var directoryName = Path.GetDirectoryName(filePath);
            if (args.Length == 1)
            {
                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    var path = "/etc/systemd/system/" + serviceName + ".service";
                    System.IO.File.WriteAllText(path, "[Unit]" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "Description=" + serviceDescription + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "[Service]" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "Type=simple" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "GuessMainPID=true" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "WorkingDirectory=" + directoryName + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "StandardOutput=journal" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "StandardError=journal" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "ExecStart=" + text + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "Restart=always" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "[Install]" + Environment.NewLine);
                    System.IO.File.AppendAllText(path, "WantedBy=multi-user.target" + Environment.NewLine);
                    systemctl("enable", serviceName);
                    systemctl("start", serviceName);
                    Console.WriteLine("Unix 下安装服务完成，如果失败请手动执行以下命令完成安装:");
                    string path2 = Path.Combine(directoryName, "SerNote.txt");
                    StringBuilder _sb = new StringBuilder(300);
                    _sb.AppendLine("服务命令参考");
                    _sb.AppendLine("[启用服务]：systemctl enable " + serviceName + ".service");
                    _sb.AppendLine("[禁用服务]：systemctl disable " + serviceName + ".service");
                    _sb.AppendLine("[启动服务]：systemctl start " + serviceName + ".service");
                    _sb.AppendLine("[重启服务]：systemctl restart " + serviceName + ".service");
                    _sb.AppendLine("[停止服务]：systemctl stop " + serviceName + ".service");
                    _sb.AppendLine("[查看状态]：systemctl status " + serviceName + ".service -l");
                    _sb.AppendLine("[查看日志]：journalctl -u " + serviceName + ".service -f -n 50");
                    string outtext = _sb.ToString();
                    Console.WriteLine(outtext);
                    System.IO.File.WriteAllText(path2, outtext);

                    return true;
                }

                if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                {
                    var text2 = "/etc/systemd/system/" + serviceName + ".service";
                    systemctl("stop", serviceName);
                    systemctl("disable", serviceName);

                    if (System.IO.File.Exists(text2)) { System.IO.File.Delete(text2); }
                    Console.WriteLine($"{serviceName} 服务在Unix 下卸载完成，如果失败请手动执行以下命令完成卸载");
                    Console.WriteLine("systemctl disable " + serviceName + ".service  //使自启动失效");
                    Console.WriteLine("rm -rf " + text2 + "  //删除服务文件");
                    return true;
                }
            }

            startRun(args);
            Thread.Sleep(-1);
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="serName"></param>
        private static void systemctl(string cmd, string serName)
        {
            try
            {
                Console.WriteLine(StartProcess("systemctl", $"{cmd} {serName}.service"));
            }
            catch
            {
                try
                {
                    Console.WriteLine(StartProcess("/usr/bin/systemctl", $"{cmd} {serName}.service"));
                }
                catch
                {
                    Console.WriteLine(StartProcess("/bin/systemctl", $"{cmd} {serName}.service"));
                }
            }
        }

        /// <summary>
        /// 运行程序，如果有命令-i或者-u则执行安装或卸载，否则执行startRun
        /// 请在Main函数中调用，服务显示名称请在Main函数增加[DisplayName()]特性，服务说明[Description]特性。
        /// Windiows下需要依赖System.ServiceProcess.ServiceController
        /// 示例代码：ServiceInstall.Run("服务名称", args, (a) => { CreateHostBuilder(args).Build().Run();}, runoption.ServiceDescription, runoption.ServiceDescription);
        /// -i 表示安装服务
        /// -u 表示卸载服务
        /// </summary>
        /// <param name="serviceName">服务名称</param>
        /// <param name="args">启动程序的参数</param>
        /// <param name="startRun">实际程序运行的函数</param>
        /// <param name="displayName">服务显示名称</param>
        /// <param name="serviceDescription">服务说明</param>
        public static void Run(string serviceName, string[] args, Action<string[]> startRun, string displayName = null, string serviceDescription = null)
        {
            var text = string.Empty;
            if (args.Length == 1)
            {
                if (args[0] == "-d")
                {
                    startRun(args);
                    return;
                }

                var stackFrame = new StackFrame(1);
                if (string.IsNullOrWhiteSpace(serviceName))
                    serviceName = stackFrame.GetMethod().DeclaringType.Assembly.GetName().Name;

                if (string.IsNullOrEmpty(displayName))
                {
                    var customAttributes = stackFrame.GetMethod().GetCustomAttributes(typeof(DisplayNameAttribute), inherit: true);
                    if (customAttributes.Length != 0)
                        displayName = (customAttributes[0] as DisplayNameAttribute).DisplayName;
                }

                if (string.IsNullOrEmpty(serviceDescription))
                {
                    var customAttributes2 = stackFrame.GetMethod().GetCustomAttributes(typeof(DescriptionAttribute), inherit: true);
                    if (customAttributes2.Length != 0)
                        serviceDescription = (customAttributes2[0] as DescriptionAttribute).Description;
                }

                text = stackFrame.GetMethod().DeclaringType.Assembly.Location;
            }

            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                if (!Process.GetCurrentProcess().MainModule.ModuleName.Equals("dotnet.exe", StringComparison.OrdinalIgnoreCase))
                {
                    text = Process.GetCurrentProcess().MainModule.FileName;
                    Environment.CurrentDirectory = Path.GetDirectoryName(text);
                }

                RunWin(text, serviceName, displayName ?? serviceName, serviceDescription ?? serviceName, args, startRun);
            }
            else
            {
                RunUnix(text, serviceName ?? serviceName, serviceDescription ?? serviceName, args, startRun);
            }
        }
        /// <summary>
        /// 执行程序
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="arguments"></param>
        /// <param name="workingDirectory">工作目录位置,为空则是当前运行目录</param>
        /// <returns></returns>
        public static string StartProcess(string fileName, string arguments, string workingDirectory = "")
        {
            var output = string.Empty;
            if (string.IsNullOrWhiteSpace(workingDirectory)) { workingDirectory = Environment.CurrentDirectory; }
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    UseShellExecute = false,
                    Arguments = arguments,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    WorkingDirectory = workingDirectory,
                    FileName = fileName
                };
                process.Start();
                process.WaitForExit();
                output = process.StandardOutput.ReadToEnd();
                string error = process.StandardError.ReadToEnd();
                if (!string.IsNullOrWhiteSpace(error))
                {
                    Console.WriteLine($"{fileName} {arguments}[执行异常]{error}");
                    if (!string.IsNullOrWhiteSpace(output))
                    {
                        Console.WriteLine($"{output}");
                    }
                    else
                    {
                        throw new Exception(error);
                    }
                }
                process.Close();
            }
            return output;
        }

        private static bool AdminRestartApp(string filePath, string[] args)
        {
            if (!IsAdministrator())
            {
                Console.WriteLine("重新以管理员启动" + filePath);
                var startInfo = new ProcessStartInfo
                {
                    UseShellExecute = true,
                    Arguments = string.Join(" ", args),
                    WorkingDirectory = Environment.CurrentDirectory,
                    FileName = filePath,
                    Verb = "runas"
                };
                try
                {
                    Process.Start(startInfo);
                }
                catch (Exception arg)
                {
                    Console.WriteLine($"重新以管理员启动失败：{arg}");
                }

                return false;
            }

            return true;
        }

        /// <summary>
        /// 判断是否是处于Administrator下允许m
        /// </summary>
        /// <returns></returns>
        private static bool IsAdministrator()
        {
            var ntIdentity = WindowsIdentity.GetCurrent();
            return new WindowsPrincipal(ntIdentity).IsInRole(WindowsBuiltInRole.Administrator);
        }
    }
}
